package mc.support.web.action.invoke;

import mc.support.web.action.aware.Aware;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA. <br/>
 * Author: Francis Yun    <br/>
 * Date: 2014-04-21  <br/>
 */
public final class InvokerMgr {

    private static final byte[] SHARED_LOCK = new byte[0];

    private static Map<Class<?>, Invoker<?>> invokerCache = new ConcurrentHashMap<>();

    private static Map<Class<? extends Aware>, Invoker<? extends Aware>> allInvokers = new ConcurrentHashMap<>();

    private static Invoker<?> defaultInvoker;

    public static void registerInvoker(Invoker<?> invoker) {
        allInvokers.put( invoker.getAwareClass(), invoker );
    }

    public static Invoker<?> getDefaultInvoker() {
        return defaultInvoker;
    }

    public static void setDefaultInvoker(Invoker<?> defaultInvoker) {
        InvokerMgr.defaultInvoker = defaultInvoker;
    }

    public static Invoker<?> resolveInvoker(Class<?> clazz) {
        Invoker<?> invoker = invokerCache.get( clazz );
        if ( null == invoker ) {
            synchronized ( SHARED_LOCK ) {
                invoker = invokerCache.get( clazz );
                if ( null == invoker ) {
                    invoker = getInvokerByPriority( clazz );
                    if ( null == invoker ) {
                        invoker = getDefaultInvoker();
                    }
                    if ( null != invoker ) {
                        invokerCache.put( clazz, invoker );
                    }
                }
            }
        }
        return invoker;
    }

    private static Invoker<?> getInvokerByPriority(Class<?> clz) {
        List<Invoker> invokers = new LinkedList<>();
        for ( Map.Entry<Class<? extends Aware>, Invoker<? extends Aware>> e : allInvokers.entrySet() ) {
            Class<? extends Aware> aware = e.getKey();
            Invoker<? extends Aware> invoker = e.getValue();
            if ( aware.isAssignableFrom( clz ) ) {
                invokers.add( invoker );
            }
        }
        Invoker<?> invoker = null;
        int maxPriority = Integer.MIN_VALUE;
        for ( Invoker ivk : invokers ) {
            int priority = ivk.getPriority();
            if ( maxPriority < ivk.getPriority() ) {
                invoker = ivk;
                maxPriority = priority;
            }
        }
        return invoker;
    }

}
